package xyz.icxl.android.architecture.helper

import android.annotation.SuppressLint
import com.squareup.moshi.*
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import org.json.JSONObject
import java.io.IOException
import java.lang.reflect.Type
import java.math.BigDecimal

/**
 * 文件描述：moshi 帮助类
 *
 * 文件创建人：ChenXinLei
 *
 * 文件创建人联系方式：502616659(QQ)
 *
 * 创建时间：2021 年 09月 13 日
 *
 * 文件版本：v1.0
 *
 * 版本描述：创建文件
 */
object MoshiHelper {
    private val mMoshi = Moshi.Builder()
        .add(MoshiArrayListJsonAdapter.FACTORY)
        .add(HashMapJsonAdapter.FACTORY)
        .add(BigDecimalAdapter)
        .add(KotlinJsonAdapterFactory())
        .build()

    fun get(): Moshi = mMoshi

    /**
     * 初始化帮助类
     */
    @SuppressLint("CheckResult")
    fun initMoshi() {
        Moshi.Builder()
            .add(MoshiArrayListJsonAdapter.FACTORY)
            .add(HashMapJsonAdapter.FACTORY)
            .add(BigDecimalAdapter)
            .add(KotlinJsonAdapterFactory())
            .build()
    }

    fun toJsonStr(any: Any?): String {
        if (null == any) return "{}"
        return mMoshi.adapter(any.javaClass).toJson(any)
    }

    fun <T : Map<String, Any?>> toJsonStrByHashMap(map: T?): String {
        if (null == map) return "{}"
        return mMoshi.adapter<Map<String, Any?>>(
            Types.newParameterizedType(
                MutableMap::class.java,
                String::class.java,
                Any::class.java
            )
        ).indent("  ").toJson(map)
    }

    fun <T> toMutableList(json: String, clazz: Class<T>): MutableList<T> {
        return mMoshi.adapter<MutableList<T>>(
            Types.newParameterizedType(
                MutableList::class.java,
                clazz
            )
        ).fromJson(json) ?: arrayListOf()
    }

    fun <K, V> toMutableMap(key: Class<K>, value: Class<V>, json: String): MutableMap<K, V> {
        return mMoshi.adapter<MutableMap<K, V>>(
            Types.newParameterizedType(
                MutableMap::class.java,
                key, value
            )
        ).fromJson(json) ?: hashMapOf()
    }

    fun <T> toBean(json: String, clazz: Class<T>): T? {
        return mMoshi.adapter(clazz).fromJson(json)
    }

    /**
     *  自定义 Moshi 的ArrayList适配器
     */
    abstract class MoshiArrayListJsonAdapter<C : MutableCollection<T>?, T> private constructor(
        private val elementAdapter: JsonAdapter<T>
    ) :
        JsonAdapter<C>() {
        abstract fun newCollection(): C

        @Throws(IOException::class)
        override fun fromJson(reader: JsonReader): C {
            val result = newCollection()
            reader.beginArray()
            while (reader.hasNext()) {
                result?.add(elementAdapter.fromJson(reader)!!)
            }
            reader.endArray()
            return result
        }

        @Throws(IOException::class)
        override fun toJson(writer: JsonWriter, value: C?) {
            writer.beginArray()
            for (element in value!!) {
                elementAdapter.toJson(writer, element)
            }
            writer.endArray()
        }

        override fun toString(): String {
            return "$elementAdapter.collection()"
        }

        companion object {
            val FACTORY = Factory { type, annotations, moshi ->
                val rawType = Types.getRawType(type)
                if (annotations.isNotEmpty()) return@Factory null
                if (rawType == ArrayList::class.java) {
                    return@Factory newArrayListAdapter<Any>(
                        type,
                        moshi
                    ).nullSafe()
                }
                null
            }

            private fun <T> newArrayListAdapter(
                type: Type,
                moshi: Moshi
            ): JsonAdapter<MutableCollection<T>> {
                val elementType =
                    Types.collectionElementType(
                        type,
                        MutableCollection::class.java
                    )

                val elementAdapter: JsonAdapter<T> = moshi.adapter(elementType)

                return object :
                    MoshiArrayListJsonAdapter<MutableCollection<T>, T>(elementAdapter) {
                    override fun newCollection(): MutableCollection<T> {
                        return ArrayList()
                    }
                }
            }
        }
    }


    /**
     * Converts maps with string keys to JSON objects.
     *
     */
    class HashMapJsonAdapter<K, V>(moshi: Moshi, keyType: Type, valueType: Type) :
        JsonAdapter<HashMap<K?, V?>?>() {
        private val keyAdapter: JsonAdapter<K> = moshi.adapter(keyType)
        private val valueAdapter: JsonAdapter<V> = moshi.adapter(valueType)

        @Throws(IOException::class)
        override fun toJson(writer: JsonWriter, value: HashMap<K?, V?>?) {
            writer.beginObject()
            for (entry: Map.Entry<K?, V?> in value!!.entries) {
                if (entry.key == null) {
                    throw JsonDataException("Map key is null at " + writer.path)
                }
                writer.promoteValueToName()
                keyAdapter.toJson(writer, entry.key)
                valueAdapter.toJson(writer, entry.value)
            }
            writer.endObject()
        }

        @Throws(IOException::class)
        override fun fromJson(reader: JsonReader): HashMap<K?, V?> {
            val result = HashMap<K?, V?>()
            reader.beginObject()
            while (reader.hasNext()) {
                reader.promoteNameToValue()
                val name = keyAdapter.fromJson(reader)
                val value = valueAdapter.fromJson(reader)
                val replaced = result.put(name, value)
                if (replaced != null) {
                    throw JsonDataException(
                        "Map key '"
                                + name
                                + "' has multiple values at path "
                                + reader.path
                                + ": "
                                + replaced
                                + " and "
                                + value
                    )
                }
            }
            reader.endObject()
            return result
        }

        override fun toString(): String {
            return "JsonAdapter($keyAdapter=$valueAdapter)"
        }

        companion object {
            val FACTORY: Factory =
                Factory { type, annotations, moshi ->
                    val rawType = Types.getRawType(type)
                    if (annotations.isNotEmpty()) return@Factory null
                    if (rawType != java.util.HashMap::class.java) return@Factory null
                    val keyAndValue = if (type === java.util.Properties::class.java) arrayOf<Type>(
                        String::class.java,
                        String::class.java
                    ) else {
                        arrayOf<Type>(Any::class.java, Any::class.java)
                    }
                    HashMapJsonAdapter<Any?, Any>(
                        moshi,
                        keyAndValue[0],
                        keyAndValue[1]
                    ).nullSafe()
                }
        }

    }


    object BigDecimalAdapter {
        @FromJson
        fun fromJson(string: String?): BigDecimal =
            if (string.isNullOrEmpty()) BigDecimal.ZERO else BigDecimal(string)

        @ToJson
        fun toJson(value: BigDecimal) = value.toString()
    }

}